﻿/*
 *  ChgDoc CMS - GNU General Public License, version 3 (GPL-3.0)
 *  Copyright (C)  2012. Ian Quigley
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Reflection;
using System.Web;
using ChgDoc.Entity;
using log4net;
using log4net.Repository.Hierarchy;
using SqliteORM;
using ChgDoc.Razor;
using ChgDoc.Extension;
using ChgDoc.Render;
using StackExchange.Profiling;
using System.Collections;
using System.Collections.Concurrent;

namespace ChgDoc
{
	public class Config
	{
        private static string _baseUrl = null;
        public static string BaseUrl { get { return (_baseUrl ?? (_baseUrl = Config.Current["BaseUrl"] )); } }

        private string _localUploadFolder;
        public string LocalUploadFolder { get { return (_localUploadFolder ?? (_localUploadFolder = Config.Current["UploadFolder"])); } }

        private bool? _disableSetup;
        public bool DisableSetup
        { 
            get 
            {
                if (_disableSetup.HasValue)
                    return _disableSetup.Value;

                if (!Current.Defines("ChgDoc.DisableSetup"))
                    return false;

                _disableSetup = bool.Parse(Current["ChgDoc.DisableSetup"]);
                return _disableSetup.Value;
            }
        }

          
        private static bool? _pageCachging;
        public static bool PageCachingEnabled 
        { 
            get 
            {
                if (!_pageCachging.HasValue)
                {
                    bool b;
                    if (bool.TryParse(Config.Current["PageCaching"], out b))
                        b = false;

                    _pageCachging = b;
                }

                return _pageCachging.Value;
            } 
        }

		public static ILog Log = LogManager.GetLogger( typeof (Config) );
        private static string _driveLetter = null;
		
		public static void Initialize( Assembly application)
		{
            log4net.Config.XmlConfigurator.Configure();
            ChgDoc.Config.Log.Info("Application started");        

            DbConnection.Initialise(dbFilename(), application);
            
			DbConnection.SqlListeners += new DbConnection.SqlListener( DbConnection_SqlListeners );

            bool dbException = false;
            try
            {
                foreach (string str in DbConnection.VerifySchema())
                    Log.Info("Db.VerifySchema: " + str);
            }
            catch
            {
                dbException = true;
                _baseUrl = "";
            }

            if (dbException)
                HttpContext.Current.Response.Redirect("~/edit/setuptasks.aspx", true);

            RenderEngine.Add( "Razor", "Razor template engine" );
            RenderEngine.Add( "Plain", "Plain text" );
            RazorEngine.Razor.AddResolver(new RazorResolver());

            if (string.IsNullOrEmpty(Config.Current["ChgDoc.Version"]))
                Config.Current["ChgDoc.Version"] = "1";
    	}


		static void DbConnection_SqlListeners( string sql )
		{
			Log.Info( "SQL: " + sql );
		}

		private static string DriveLetter
		{
			get
			{
				if (_driveLetter == null)
				{
					_driveLetter = HttpContext.Current.Server.MapPath( "" );
                    _driveLetter = _driveLetter.Substring( 0, 1 );
				}
				return _driveLetter;				
			}   
		}
        private static string dbFilename()
        {
            var str = string.Format( ConfigurationManager.AppSettings[ "DbConnection" ] ?? "", DriveLetter );
            Log.Info("DbConnectionString: " + str);
            return str;
        }

        private static Config _current;
		public static Config Current
		{
			get
			{
                return (_current ?? (_current = new Config()));
			}
		}

        public string Url(string subfolder)
        {
            return BaseUrl + subfolder;
        }
		public DateTime NowUtc
		{
			get { return DateTime.UtcNow; }
		}

     	public static string UploadFolder
		{
			get 
            {
                return HttpContext.Current.Server.MapPath( Config.Current.LocalUploadFolder); 
            }
		}

		public static string UploadFolderUrl
		{
            get { return (new Uri( new Uri( Config.BaseUrl), Config.Current.LocalUploadFolder)).AbsolutePath; }
		}


        private class ValueFunction
        {
            public string Value;
            public Action<string> Set = str => { };
        }

        private Dictionary<string, ValueFunction> _settings = new Dictionary<string, ValueFunction>();

		public string this [string name]
		{
			get
			{
				if (_settings.ContainsKey( name ))
					return _settings[ name ].Value ;

				var dbValue = Settings.GetValue( name );
				if (dbValue != null)
                    _settings.Add(name, new ValueFunction() { Value = dbValue, Set = FunctionFor(name) });

				return dbValue;
			}
			set
			{
                var valFunc = new ValueFunction() { Value = value, Set = FunctionFor(name) };
                _settings[name] = valFunc;
                valFunc.Set(value);
				Settings.SetValue( name, valFunc.Value );
			}
		}
        
        private bool Defines(string name)
        {
            return _settings.ContainsKey(name);
        }
        

        private Action<string> FunctionFor(string name)
        {
            switch (name.ToLower())
            {
                case "baseurl":
                    return str => _baseUrl = str;
                case "pagecaching":
                    return str => _pageCachging = bool.Parse(str);
                case "uploadfolder":
                    return str => _localUploadFolder = str;
                case "chgdoc.disablesetup":
                    return str => _disableSetup = bool.Parse(str);
                default:
                    return str => { };
            }
        }

        internal static void ClearCaches()
        {
            // Clear cache
            foreach (DictionaryEntry de in HttpContext.Current.Cache)
                HttpContext.Current.Cache.Remove(de.Key.ToString());

            // Clear Razor cache
            FieldInfo fi = RazorEngine.Razor.DefaultTemplateService.GetType().GetField("templateCache", BindingFlags.NonPublic | BindingFlags.Instance);
            if (fi != null)
                fi.SetValue(RazorEngine.Razor.DefaultTemplateService, new ConcurrentDictionary<string, RazorEngine.Templating.ITemplate>());
        }
    }

	public static class Log4NetExtension
	{
		public static T LogResultInfo<T>(this ILog logger, Func<T> action) where T : class
		{
			var result = action();
			Config.Log.Info( "Result: " + (result == null ? "<null>" : result.ToString()));
			return result;
		}
	}
}
